package  networkSimulation.framework

/**
 * This class represents a graph
 *
 * @author  Tim Caljé
 *          Jeroen De Raedt
 *          Jorick Van Aken
 */
abstract class Graph[N <% Node, E <% Edge[N]]() extends Observable{

  // the list of nodes
  var nodes = List[N]()
  // the list of edges
  var edges = List[E]()

  /**
   * Add a node to the graph
   * @param node: N
   *        The node which should be added to the graph
   */
  def addNode(node : N) {    
    if (((this.nodes filter (_.name == node.name)).length)>0)
      throw new IllegalArgumentException("Error: Duplicate node found. All nodes must have a unique identifier!");
    nodes ::= node
  }

  /**
   * Add an edge to the graph
   * @param edge: E
   *        The edge which should be added to the graph
   */
  def addEdge(edge : E) {
    edges ::= edge
  }

  /**
   * Remove an edge from the graph
   * @param edge: E
   *        The edge which should be deleted
   */
  def removeEdge(edge : E ) {
    edges = edges filter (edge !=)
  }

  /**
   * Remove a node from the graph
   * @param node: N
   *        The node which should be deleted
   */
  def removeNode(node : N) {
    this.edges = this.edges filter (_.from != node) filter (_.to != node)
    this.nodes = this.nodes filter (node !=)
  }

  /**
   * Abstract method which retrieves the neighbours of a given node
   * @param   n : N
   *          The node of which we want to retrieve the neighbours
   * @return  List[N]
   *          A list of neighbour nodes
   */
  def getNeighbours(n : N) : List[N]

  /**
   * Abstract method which retrieves the edges linking to a given node
   * @param   n : N
   *          The node of which we want to retrieve the linking nodes
   * @return  List[N]
   *          A list of linking edges to the given node
   */
  def getEdgesTo(n : N) : List[E]

  /**
   * Get the type of this graph as a String
   */
  def getGraphType() : String = "Graph"

  /**
   * Convert the graph to XML
   */
  def toXml(): xml.Elem  = {
    <graph tpe={this.getGraphType()}>
      <nodes>{
        this.nodes.map(n => n.toXml())
      }</nodes>
      <edges>{
        this.edges.map(e => e.toXml())
      }</edges>
    </graph>
  }

  /**
   * Reset the graph: clear all nodes and edges
   */
  def resetGraph() {
    nodes = List[N]()
    edges = List[E]()
  }

  /**
   * Retrieve a node by a given name
   * @param   name: String
   *          The name of the node which should be retrieved
   * @return  N
   *          The found node
   */
  def getNode(name: String) : N = {
    var results :List[N] = this.nodes.filter(n => n.name.equals(name))
    if(results.size == 1) {
      results(0)
    } else {
      throw new Exception("Node not found: " + name)
    }
  }

  /**
   * Get all edges between 2 given nodes
   */
  def getEdge(from: N, to: N) : List[E] = {   
    (edges filter (_.from == from)) filter (_.to == to)
  }

}